package kube_client

import (
	"context"
	"errors"
	"fmt"
	"go.uber.org/zap"
	appsV1 "k8s.io/api/apps/v1"
	apisCoreV1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	apisMetaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"strconv"
	"virtual-resource-schedule-service/model/kube_pod"
)

// IPodK8SClient 定义我们需要使用的K8S调用客户端的逻辑接口
type IPodK8SClient interface {
	// CreatePodToK8SCluster 创建POD到K8S集群中
	CreatePodToK8SCluster(podInfo *kube_pod.Pod) (*kube_pod.Pod, error)
	// StartPodFromK8SCluster 开机
	StartPodFromK8SCluster(req *kube_pod.StartPodReq) (*kube_pod.StartPodResp, error)
	// ShutdownPodFromK8SCluster 关机
	ShutdownPodFromK8SCluster(req *kube_pod.ShutdownPodReq) (*kube_pod.ShutdownPodResp, error)
}

type PodK8SClient struct {
	K8sClientSet *kubernetes.Clientset
	deployment   *appsV1.Deployment
}

// NewPodK8SClient 初始化K8S客户端
func NewPodK8SClient(K8SClientSet *kubernetes.Clientset) *PodK8SClient {
	zap.S().Info("NewPodK8SClient started ... ...")
	return &PodK8SClient{
		K8sClientSet: K8SClientSet,
		deployment:   &appsV1.Deployment{},
	}
}

// getContainerPort 获得容器端口信息
func (client *PodK8SClient) getContainerPort(podInfo *kube_pod.Pod) (
	containerPort []apisCoreV1.ContainerPort) {

	for _, v := range podInfo.PodPort {
		containerPort = append(containerPort, apisCoreV1.ContainerPort{
			Name:          "port-" + strconv.FormatInt(int64(v.ContainerPort), 10),
			ContainerPort: v.ContainerPort,
			Protocol:      client.getProtocol(v.Protocol),
		})
	}
	return containerPort
}

// getProtocol 获得容器端口对应的协议
func (client *PodK8SClient) getProtocol(protocol string) apisCoreV1.Protocol {
	switch protocol {
	case "TCP":
		return "TCP"
	case "UDP":
		return "UDP"
	case "SCTP":
		return "SCTP"
	default:
		return "TCP"
	}
}

// getEnv 获取容器环境信息
func (client *PodK8SClient) getEnv(podInfo *kube_pod.Pod) (
	envVar []apisCoreV1.EnvVar) {

	for _, v := range podInfo.PodEnv {
		envVar = append(envVar, apisCoreV1.EnvVar{
			Name:      v.EnvKey,
			Value:     v.EnvValue,
			ValueFrom: nil,
		})
	}
	return envVar
}

// getResources 设置资源限制
func (client *PodK8SClient) getResources(podInfo *kube_pod.Pod) (
	source apisCoreV1.ResourceRequirements) {
	// 调整request
	if podInfo.PodCpuMin > 0 && podInfo.PodMemoryMin > 0 {
		// 最大能够使用的资源数
		source.Requests = apisCoreV1.ResourceList{
			"cpu": resource.MustParse(strconv.FormatFloat(
				float64(podInfo.PodCpuMin), 'f', 6, 64)),
			"memory": resource.MustParse(strconv.FormatFloat(
				float64(podInfo.PodMemoryMin), 'f', 6, 64)),
		}
	}
	return source
}

// 镜像拉取策略
func (client *PodK8SClient) getImagePullPolicy(podInfo *kube_pod.Pod) apisCoreV1.PullPolicy {
	switch podInfo.PodPullPolicy {
	case "Always":
		return "Always"
	case "Never":
		return "Never"
	case "IfNotPresent":
		return "IfNotPresent"
	default:
		return "Always"
	}
}

func (client *PodK8SClient) SetDeployment(podInfo *kube_pod.Pod) (*appsV1.Deployment, error) {
	deployment := &appsV1.Deployment{}
	// 定义版本信息
	deployment.TypeMeta = apisMetaV1.TypeMeta{
		Kind:       "Deployment",
		APIVersion: "apps/v1",
	}
	podName := fmt.Sprintf("%s-%s", podInfo.PodName, podInfo.PodCustomerId)
	deployment.ObjectMeta = apisMetaV1.ObjectMeta{
		Name:      podName,
		Namespace: podInfo.PodNamespace,
		Labels: map[string]string{
			"app-name":  podName,
			"user-id":   podInfo.PodCustomerId,
			"tenant-id": podInfo.PodTenantId,
		},
	}
	// 名称需要组合上用户的ID信息
	deployment.Name = podName
	deployment.Spec = appsV1.DeploymentSpec{
		// 副本个数
		Replicas: &podInfo.PodReplicas,
		// 选择器
		Selector: &apisMetaV1.LabelSelector{
			MatchLabels: map[string]string{
				"app-name": podName,
			},
			MatchExpressions: nil,
		},
		Template: apisCoreV1.PodTemplateSpec{
			ObjectMeta: apisMetaV1.ObjectMeta{
				Labels: map[string]string{
					"app-name": podName,
				},
			},
			Spec: apisCoreV1.PodSpec{
				// 容器的模版
				Containers: []apisCoreV1.Container{
					{
						Name:            podName,
						Image:           podInfo.PodImage,
						Ports:           client.getContainerPort(podInfo),
						Env:             client.getEnv(podInfo),
						Resources:       client.getResources(podInfo),
						ImagePullPolicy: client.getImagePullPolicy(podInfo),
					},
				},
			},
		},
		Strategy:                appsV1.DeploymentStrategy{},
		MinReadySeconds:         0,
		RevisionHistoryLimit:    nil,
		Paused:                  false,
		ProgressDeadlineSeconds: nil,
	}
	return deployment, nil
}

// CreatePodToK8SCluster 创建Pod到K8S集群中
func (client *PodK8SClient) CreatePodToK8SCluster(podInfo *kube_pod.Pod) (pod *kube_pod.Pod, err error) {
	// 设置Deployment基础信息
	deployment, _ := client.SetDeployment(podInfo)
	// 调用api-server创建POD，这个时候的POD的副本为0，需要用户开机后再修改为1
	if _, err = client.K8sClientSet.AppsV1().Deployments(
		podInfo.PodNamespace).Get(context.TODO(), podInfo.PodName, apisMetaV1.GetOptions{}); err != nil {
		if _, err = client.K8sClientSet.AppsV1().Deployments(
			podInfo.PodNamespace).Create(
			context.TODO(), deployment, apisMetaV1.CreateOptions{}); err != nil {
			zap.S().Error(err)
			return podInfo, err
		}
		// @TODO 完成业务逻辑
		zap.S().Info("Created Pod Success !", podInfo.PodName)
		return podInfo, nil
	} else {
		// @TODO 完成异常业务逻辑
		zap.S().Info("Created Pod Error ! Pod Already Exists ", podInfo.PodName)
		return podInfo, errors.New("Pod Already Exists" + podInfo.PodName)
	}
}

// StartPodFromK8SCluster 开启容器
func (client *PodK8SClient) StartPodFromK8SCluster(req *kube_pod.StartPodReq) (
	*kube_pod.StartPodResp, error) {
	// 获取主要的参数
	// deploymentName 与 podName保持一致
	deploymentName := req.PodName
	namespace := req.PodNamespace

	rsp := &kube_pod.StartPodResp{}
	rsp.PodName = req.PodName

	deploymentClient := client.K8sClientSet.AppsV1().Deployments(namespace)
	// 修改Deployment的副本数
	deployment, err := deploymentClient.Get(
		context.TODO(), deploymentName, apisMetaV1.GetOptions{})
	if err != nil {
		fmt.Printf("获取Deployment失败: %s\n", err.Error())
		rsp.Started = false
		return rsp, errors.New("获取容器基础信息失败")
	}
	// 将副本数设置为1
	*deployment.Spec.Replicas = 1
	// 更新Deployment
	_, err = deploymentClient.Update(
		context.TODO(), deployment, apisMetaV1.UpdateOptions{})
	if err != nil {
		fmt.Printf("开机失败: %s\n", err.Error())
		rsp.Started = false
		return rsp, errors.New("容器开机失败")
	}
	rsp.Started = true
	return rsp, nil
}

func (client *PodK8SClient) ShutdownPodFromK8SCluster(req *kube_pod.ShutdownPodReq) (
	*kube_pod.ShutdownPodResp, error) {
	// 获取主要的参数
	// deploymentName 与 podName保持一致
	deploymentName := req.PodName
	namespace := req.PodNamespace

	rsp := &kube_pod.ShutdownPodResp{}
	rsp.PodName = req.PodName

	deploymentClient := client.K8sClientSet.AppsV1().Deployments(namespace)
	// 修改Deployment的副本数
	deployment, err := deploymentClient.Get(
		context.TODO(), deploymentName, apisMetaV1.GetOptions{})
	if err != nil {
		fmt.Printf("获取Deployment失败: %s\n", err.Error())
		rsp.Shutdown = false
		return rsp, errors.New("获取容器基础信息失败")
	}
	// 将副本数设置为1
	*deployment.Spec.Replicas = 0
	// 更新Deployment
	_, err = deploymentClient.Update(
		context.TODO(), deployment, apisMetaV1.UpdateOptions{})
	if err != nil {
		fmt.Printf("关机失败: %s\n", err.Error())
		rsp.Shutdown = false
		return rsp, errors.New("容器关机失败")
	}
	rsp.Shutdown = true
	return rsp, nil
}
